#pragma once
#include "Sock.hpp"
#include "log.hpp"
#include "Util.hpp"
#include <functional>
#include <unordered_map>
#include "Epoller.hpp"

class Connection;

using fun_t = function<int(Connection *)>;
using callback_t = function<int(Connection *, std::string &)>;

class Connection
{
public:
    TcpServer *_ser;
    string _inbuffer;
    string _outbuffer;
    fun_t _reader;
    fun_t _writer;
    fun_t _excepter;
    int _sock;
    Connection(int sock, TcpServer *ser) : _sock(sock), _ser(ser) {}
    void SetReader(fun_t reader)
    {
        _reader = reader;
    }
    void SetWriter(fun_t writer)
    {
        _writer = writer;
    }
    void SetExcepter(fun_t excepter)
    {
        _excepter = excepter;
    }
};

class TcpServer
{
public:
    TcpServer(callback_t cal, int port)
        : _cal(cal)
    {
        // 监听套接字
        _listenfd = Sock::Socket();
        Sock::Bind(_listenfd, port);
        Sock::Listen(_listenfd);
        // epoll
        _epfd = Epoller::CreateEpoller();
        AddConnection(_listenfd, EPOLLIN | EPOLLET,
                      bind(&TcpServer::Accepter, this, placeholders::_1), nullptr, nullptr);
    }

    void AddConnection(int sockfd, uint32_t event, fun_t reader, fun_t writer, fun_t excepter)
    {
        if (event & EPOLLET)
        {
            Util::SetNoBlock(sockfd);
        }

        Epoller::AddToEpoll(_epfd, sockfd, event);
        Connection *con = new Connection(sockfd, this);
        con->SetExcepter(excepter);
        con->SetReader(reader);
        con->SetWriter(writer);
        connections.insert({sockfd, con});
        Log(DEBUG, "添加新连接:%d", sockfd);
    }

    int TcpReader(Connection *con)
    {
        while (true)
        {
            char buff[1024];
            ssize_t s = read(con->_sock, buff, sizeof(buff) - 1);
            if (s > 0)
            {
                buff[s] = '\0';
                con->_inbuffer += buff;
                cout << con->_inbuffer << endl;
            }
            else if (s == 0)
            {
                Log(DEBUG, "%d:退出了", con->_sock);
                con->_excepter(con);
                break;
            }
            else
            {
                if (errno == EINTR)
                {
                    continue;
                }
                else if (errno == EAGAIN || errno == EWOULDBLOCK)
                {
                    break;
                }
                else
                {
                    Log(DEBUG, "read error:%d %s", errno, strerror(errno));
                    con->_excepter(con);
                    break;
                }
            }
        }
        return 0;
    }
    int TcpWriter(Connection *con)
    {
        while (true)
        {
            ssize_t s = write(con->_sock, con->_outbuffer.c_str(), con->_outbuffer.size());
            if (s > 0)
            {
                con->_outbuffer.erase(0, s);
            }
            else
            {
                if (errno == EINTR)
                    continue;
                else if (errno == EAGAIN || errno == EWOULDBLOCK)
                {
                    break;
                }
                else
                {
                    Log(DEBUG, "write errno: %d, %s", errno, strerror(errno));
                    con->_excepter(con);
                    break;
                }
            }
        }
        return 0;
    }
    int TcpExcepter(Connection *con)
    {
        if (!IsExist(con->_sock))
        {
            return 0;
        }
        Epoller::DelEvent(_epfd, con->_sock);
        int sock = con->_sock;
        close(sock);
        delete connections[sock];
        connections.erase(sock);
        Log(DEBUG, "%d 已经移除了", sock);
        return 0;
    }

    bool IsExist(int sock)
    {
        auto iter = connections.find(sock);
        if (iter == connections.end())
        {
            return false;
        }
        return true;
    }

    void DisPatcher()
    {
        int n = Epoller::LoopOnce(_epfd, revs, revs_num);
        for (int i = 0; i < n; ++i)
        {
            int sock = revs[i].data.fd;
            uint32_t revent = revs[i].events;
            // 错误到reader和write中处理
            if (revent & EPOLLHUP)
                revent |= (EPOLLIN | EPOLLOUT);
            if (revent & EPOLLERR)
                revent |= (EPOLLIN | EPOLLOUT);
            if (revent && revent & EPOLLIN)
            {
                if (IsExist(sock) && connections[sock]->_reader)
                {
                    connections[sock]->_reader(connections[sock]);
                }
            }
            if (revent && revent & EPOLLOUT)
            {
                if (IsExist(sock) && connections[sock]->_writer)
                {
                    connections[sock]->_writer(connections[sock]);
                }
            }
        }
    }

    void Run()
    {
        while (true)
        {
            DisPatcher();
        }
    }

    void EnableReadWrite(int sock, bool readable, bool writeable)
    {
        uint32_t event = 0;
        if (readable)
        {
            event |= EPOLLIN;
        }
        if (writeable)
        {
            event |= EPOLLOUT;
        }
        Epoller::ModEvent(_epfd, sock, event);
    }
    int Accepter(Connection *con)
    {
        string ip;
        uint16_t port;
        int sockfd = Sock::Accept(_listenfd, ip, port);
        if (sockfd < 0)
        {
            Log(WARNING, "accept error");
            return -1;
        }
        Log(DEBUG, "获取到一个新连接: %s:%d", ip.c_str(), port);
        AddConnection(sockfd, EPOLLIN | EPOLLET,
                      std::bind(&TcpServer::TcpReader, this, placeholders::_1),
                      std::bind(&TcpServer::TcpWriter, this, placeholders::_1),
                      std::bind(&TcpServer::TcpExcepter, this, placeholders::_1));
        return 0;
    }

    ~TcpServer()
    {
        if (_listenfd)
        {
            close(_listenfd);
        }
        if (_epfd < 0)
        {
            close(_epfd);
        }
        delete[] revs;
    }

private:
    static const int revs_num = 64;
    int _epfd;
    int _listenfd;
    unordered_map<int, Connection *> connections;
    epoll_event *revs = nullptr;
    callback_t _cal;
};
